/*
    SDL - Simple DirectMedia Layer
    Partial implementation of SDL library (originally written by
    Sam Lantinga <slouken@libsdl.org>) for the particular purpose to support
    Prequengine (http://code.google.com/p/prequengine/) on BlackBerry(tm)
    tablets and smartphones.

    Copyright (C) 2013  xlamsp

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

    xlamsp@gmail.com
*/

#include "SDL_video.h"
#include "SDL_sysvideo.h"
#include "SDL_pixels.h"


SDL_Surface * SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask,
        Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
{
    SDL_VideoDevice *video = current_video;
    SDL_Surface *surface;

    SDL_Log(DEBUG1, "flags=0x%x width=%d height=%d depth=%d Rmask=0x%x Gmask=0x%x Bmask=0x%x Amask=0x%x",
            flags, width, height, depth, Rmask, Gmask, Bmask, Amask);

    if(depth != 8 && depth != 32) {
        SDL_Log(ERROR, "unsupported depth: %d!", depth);
        return NULL;
    }

    /* Allocate the surface */
    surface = (SDL_Surface *)SDL_calloc(1, sizeof(*surface));
    if(surface == NULL) {
        SDL_Log(ERROR, "failed to allocate %d bytes for surface!", sizeof(*surface));
        return (NULL);
    }

    surface->format = SDL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask);
    if(surface->format == NULL) {
        SDL_Log(ERROR, "failed to allocate format!");
        SDL_free(surface);
        return (NULL);
    }

    surface->w = width;
    surface->h = height;

    /* Get the pixels */
    if(surface->w && surface->h) {
        if(depth == 32) {
            int ret;

            if(Amask) {
                surface->flags |= SDL_SRCALPHA;
            }

            if(flags & SDL_HWACCEL) {
                surface->flags |= SDL_HWACCEL;
            }

            ret = video->AllocHWSurface(video, surface);

            if(ret != 0) {
                SDL_Log(ERROR, "failed to create pixmap!");
                SDL_FreeSurface(surface);
                return (NULL);
            }
        } else {
            surface->pitch = SDL_CalculatePitch(surface);
            surface->pixels = SDL_malloc(surface->h * surface->pitch);
            if(surface->pixels == NULL) {
                SDL_Log(ERROR, "failed to allocate %d bytes for pixels!",
                    surface->h * surface->pitch);
                SDL_FreeSurface(surface);
                return (NULL);
            }
            /* This is important for bitmaps */
            SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
        }
    }

    SDL_Log(DEBUG1, "success, surface=0x%p", surface);
    return surface;
}

SDL_Surface * SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch,
        Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
{
    SDL_Surface *surface;

    SDL_Log(DEBUG1, "pixels=0x%p width=%d height=%d depth=%d pitch=%d Rmask=0x%x Gmask=0x%x Bmask=0x%x Amask=0x%x",
            pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask);

    if(depth != 8) {
        SDL_Log(ERROR, "unsupported depth: %d!", depth);
        return NULL;
    }

    surface = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
    if(surface == NULL) {
        SDL_Log(ERROR, "failed");
    } else {
        surface->flags |= SDL_PREALLOC;
        surface->pixels = pixels;
        surface->w = width;
        surface->h = height;
        surface->pitch = pitch;
    }

    return surface;
}

int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 value)
{
    SDL_Log(DEBUG1, "surface=0x%p flag=0x%x value=%u", surface, flag, (Uint32)value);

    if(flag & SDL_SRCALPHA) {
        surface->flags |= SDL_SRCALPHA;
        surface->format->alpha = value;
    } else {
        surface->flags &= ~SDL_SRCALPHA;
        surface->format->alpha = SDL_ALPHA_OPAQUE;
    }

    return 0;
}

/*
 * Set up a blit between two surfaces -- split into three parts:
 * The upper part, SDL_UpperBlit(), performs clipping and rectangle
 * verification.  The lower part is a pointer to a low level
 * accelerated blitting function.
 *
 * These parts are separated out and each used internally by this
 * library in the optimimum places.  They are exported so that if
 * you know exactly what you are doing, you can optimize your code
 * by calling the one(s) you need.
 */
int SDL_LowerBlit (SDL_Surface *src, SDL_Rect *srcrect,
                SDL_Surface *dst, SDL_Rect *dstrect)
{
    SDL_VideoDevice *video = current_video;
    int alpha = src->format->alpha;

    if(src->hwdata && dst->hwdata) {
        /* blit from 32-bit to 32-bit surface */
        return video->BlitHWSurface(video, src, srcrect, dst, dstrect);
    } else if(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 32) {
        /* blit from 8-bit to 32-bit surface */
        int i, j;
        for(i = 0; i < srcrect->h; i++) {
            Uint8 *p_src = (Uint8*)(src->pixels) + (i + srcrect->y) * src->pitch +
                srcrect->x * src->format->BytesPerPixel;
            Uint8 *p_dst = (Uint8*)(dst->pixels) + (i + dstrect->y) * dst->pitch +
                dstrect->x * dst->format->BytesPerPixel;
            for(j = 0; j < srcrect->w; j++) {
                *(p_dst++) = src->format->palette->colors[*p_src].b;
                *(p_dst++) = src->format->palette->colors[*p_src].g;
                *(p_dst++) = src->format->palette->colors[*p_src].r;
                *(p_dst++) = alpha;
                p_src++;
            }
        }
    } else {
        SDL_Log(ERROR, "unsupported blitting!");
        return -1;
    }

    return 0;
}

int SDL_UpperBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
{
    SDL_Rect fulldst;
    int srcx, srcy, w, h;

    if(!src || !dst) {
        SDL_Log(ERROR, "passed a NULL surface");
        return(-1);
    }

    /* If the destination rectangle is NULL, use the entire dest surface */
    if(dstrect == NULL) {
        fulldst.x = fulldst.y = 0;
        dstrect = &fulldst;
    }

    /* clip the source rectangle to the source surface */
    if(srcrect) {
        int maxw, maxh;

        srcx = srcrect->x;
        w = srcrect->w;
        if(srcx < 0) {
            w += srcx;
            dstrect->x -= srcx;
            srcx = 0;
        }
        maxw = src->w - srcx;
        if(maxw < w) {
            w = maxw;
        }

        srcy = srcrect->y;
        h = srcrect->h;
        if(srcy < 0) {
            h += srcy;
            dstrect->y -= srcy;
            srcy = 0;
        }
        maxh = src->h - srcy;
        if(maxh < h) {
            h = maxh;
        }

    } else {
        srcx = srcy = 0;
        w = src->w;
        h = src->h;
    }

#if 0 /* will use it later, if needed */
    /* clip the destination rectangle against the clip rectangle */
    {
        SDL_Rect *clip = &dst->clip_rect;
        int dx, dy;

        dx = clip->x - dstrect->x;
        if(dx > 0) {
            w -= dx;
            dstrect->x += dx;
            srcx += dx;
        }
        dx = dstrect->x + w - clip->x - clip->w;
        if(dx > 0) {
            w -= dx;
        }

        dy = clip->y - dstrect->y;
        if(dy > 0) {
            h -= dy;
            dstrect->y += dy;
            srcy += dy;
        }
        dy = dstrect->y + h - clip->y - clip->h;
        if(dy > 0) {
            h -= dy;
        }
    }
#endif

    if(w > 0 && h > 0) {
        SDL_Rect sr;
        sr.x = srcx;
        sr.y = srcy;
        sr.w = dstrect->w = w;
        sr.h = dstrect->h = h;
        return SDL_LowerBlit(src, &sr, dst, dstrect);
    }
    dstrect->w = dstrect->h = 0;
    return 0;
}

void SDL_FreeSurface(SDL_Surface *surface)
{
    SDL_Log(DEBUG1, "entered, surface=0x%p", surface);

    if(!surface) {
        return;
    }

    if(surface->hwdata) {
        SDL_VideoDevice *video = current_video;
        video->FreeHWSurface(video, surface);
    } else {
        if(surface->pixels && !(surface->flags & SDL_PREALLOC)) {
            SDL_free(surface->pixels);
            surface->pixels = NULL;
        }
    }

    if(surface->format) {
        SDL_FreeFormat(surface->format);
        surface->format = NULL;
    }

    SDL_free(surface);
}

